package org.theSeed.fileContractAliImpl.contract.impl;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.common.auth.CredentialsProviderFactory;
import com.aliyun.oss.common.comm.Protocol;
import com.aliyun.oss.common.comm.SignVersion;
import com.aliyun.oss.internal.OSSMultipartOperation;
import com.aliyun.oss.model.*;
import lombok.extern.slf4j.Slf4j;
import org.theSeed.base.exp.BuzExp;
import org.theSeed.base.exp.ServiceExp;
import org.theSeed.base.pojo.sysEnum.SysCode;
import org.theSeed.base.utils.SeedStringUtil;
import org.theSeed.fileContract.contract.impl.FileContractAbstract;
import org.theSeed.fileContract.exp.FileUploadExp;
import org.theSeed.fileContract.pojo.dto.*;
import org.theSeed.fileContract.pojo.vo.*;
import org.theSeed.fileContract.utils.SeedFileConfigUtil;
import org.theSeed.fileContractAliImpl.pojo.prop.FileConfigAliProp;

import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 阿里文件上传适配
 */
@Slf4j
public class FileContractAliImpl extends FileContractAbstract {
    public FileContractAliImpl(String contractTypeInstance, FileConfigAliProp fileConfigAliProp, Long chipFileSize) {
        super(contractTypeInstance, fileConfigAliProp, chipFileSize);
        createClient();
    }

    private OSSClient ossClient;

    /**
     * 创建客户端
     * @return
     */
    protected void createClient(){
        FileConfigAliProp fileConfigProp = getFileConfigAliProp();

        // 创建 ClientBuilderConfiguration 实例，用于配置 OSS 客户端参数
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        // 设置签名算法版本为 V4
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
        // 设置使用 HTTPS 协议访问 OSS，保证传输安全性
        clientBuilderConfiguration.setProtocol(Protocol.HTTPS);

        // 创建 OSS 客户端实例
        ossClient = (OSSClient)OSSClientBuilder.create()
                // 默认以华东1（杭州）地域的外网访问域名为例，Endpoint填写为oss-cn-hangzhou.aliyuncs.com
                .endpoint(fileConfigProp.getEndpoint())
                // 从环境变量中获取访问凭证（需提前配置 OSS_ACCESS_KEY_ID 和 OSS_ACCESS_KEY_SECRET）
                .credentialsProvider(CredentialsProviderFactory.newDefaultCredentialProvider(fileConfigProp.getAccessKeyId(),fileConfigProp.getAccessKeySecrect()))
                // 设置客户端配置
                .clientConfiguration(clientBuilderConfiguration)
                // 以华东1（杭州）地域为例，Region填写为cn-hangzhou
                .region(fileConfigProp.getRegion())
                .build();
    }

    @Override
    public InputStream getFile(SeedGetFileDto seedGetFileDto) {
        GetObjectRequest param = new GetObjectRequest(getFileConfigAliProp().getBucketName(),seedGetFileDto.getResourceId());
        OSSObject object = ossClient.getObject(param);
        return object.getObjectContent();
    }

    @Override
    public SeedUploadFileVo uploadFile(SeedUploadFileDto seedUploadFileDto) {
        String bucketName = getFileConfigAliProp().getBucketName();
        String resourceId = SeedFileConfigUtil.createWebPathResourceId(seedUploadFileDto.getRelativePath(), FileUtil.extName(seedUploadFileDto.getFileName()));
        ossClient.putObject(bucketName, resourceId, seedUploadFileDto.getIs());
        return SeedUploadFileVo.createSonUploadVo(resourceId,getFileUrl(bucketName,resourceId));
    }

    @Override
    public SeedUploadFileVo updateFile(SeedUpdateFileDto seedUpdateFileDto) {
        String bucketName = getFileConfigAliProp().getBucketName();
        ossClient.putObject(bucketName, seedUpdateFileDto.getResourceId(), seedUpdateFileDto.getIs());
        return new SeedUploadFileVo(seedUpdateFileDto.getResourceId(),getFileUrl(bucketName,seedUpdateFileDto.getResourceId()));
    }

    @Override
    public SeedUploadFileVo deleteFile(SeedDeleteFileDto seedDeleteFileDto) {
        String bucketName = getFileConfigAliProp().getBucketName();
        ossClient.deleteObject(bucketName,seedDeleteFileDto.getResourceId());
        return new SeedUploadFileVo(seedDeleteFileDto.getResourceId(),getFileUrl(bucketName,seedDeleteFileDto.getResourceId()));
    }

    @Override
    public SeedUploadChipFileInitVo initUploadChipFile(SeedUploadChipFileInitDto seedUploadChipFileInitDto) {
        String bucketName = getFileConfigAliProp().getBucketName();
        long chipFileSize = getChipFileSize() * 1024;
        String resourceId = SeedFileConfigUtil.createWebPathResourceId(seedUploadChipFileInitDto.getRelativePath(),FileUtil.extName(seedUploadChipFileInitDto.getFileName()));
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, resourceId);
        InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
        // 返回uploadId，它是分片上传事件的唯一标识，您可以根据此uploadId来发起相关的操作，如取消分片上传、查询分片上传等。
        SeedUploadChipFileInitVo seedUploadChipFileInitVo = new SeedUploadChipFileInitVo(resourceId, chipFileSize, Double.valueOf(Math.ceil((0d + seedUploadChipFileInitDto.getFileSize()) / chipFileSize)).intValue());
        seedUploadChipFileInitVo.getExtInfo().put("uploadId",upresult.getUploadId());
        seedUploadChipFileInitVo.getExtInfo().put("bucketName",bucketName);
        return seedUploadChipFileInitVo;
    }

    @Override
    public SeedUploadFileVo uploadChipFile(SeedUploadChipFileDto seedUploadChipFileDto, SeedUploadChipFileExtDto seedUploadChipFileExtDto) {
        Map<String, Object> extInfo = (Map<String, Object>) (seedUploadChipFileExtDto.getAddExtInfo().getOrDefault("extInfo", Collections.EMPTY_MAP));
        String uploadId = (String)(extInfo).get("uploadId");
        String bucketName = (String)(extInfo).get("bucketName");
        if (ObjectUtil.isNull(uploadId))
            throw new FileUploadExp("文件分片上传失败:uploadId不能为空");

        UploadPartRequest uploadPartRequest = new UploadPartRequest();
        uploadPartRequest.setBucketName(bucketName);
        uploadPartRequest.setKey(seedUploadChipFileDto.getResourceId());
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setInputStream(seedUploadChipFileDto.getIs());
        // 设置分片大小。除了最后一个分片没有大小限制，其他的分片最小为100 KB。
        uploadPartRequest.setPartSize(seedUploadChipFileExtDto.getChipFileSize()/1024);
        // 设置分片号。每一个上传的分片都有一个分片号，取值范围是1~10000，如果超出这个范围，OSS将返回InvalidArgument的错误码。
        uploadPartRequest.setPartNumber( seedUploadChipFileDto.getChipIndex() + 1);
        // 每个分片不需要按顺序上传，甚至可以在不同客户端上传，OSS会按照分片号排序组成完整的文件。
        UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
        // 遍历分片上传。
        return new SeedUploadFileVo(seedUploadChipFileDto.getResourceId(),uploadPartResult.getETag());
    }

    @Override
    public SeedUploadFileVo mergeChipFile(MergeChipFileDto mergeChipFileDto, SeedUploadChipFileExtDto seedUploadChipFileExtDto) {
        String bucketName = (String)((Map<String, Object>) (seedUploadChipFileExtDto.getAddExtInfo().getOrDefault("extInfo", Collections.EMPTY_MAP))).get("bucketName");
        String uploadId = ((JSONObject) (seedUploadChipFileExtDto.getAddExtInfo().get("extInfo"))).getStr("uploadId");
        OSSMultipartOperation multipartOperation = ossClient.getMultipartOperation();
        ListPartsRequest cs = new ListPartsRequest(bucketName,mergeChipFileDto.getResourceId(),uploadId);
        PartListing partListing = multipartOperation.listParts(cs);
        List<PartETag> etagList = partListing.getParts().stream()
                .map(ele -> {
                    return new PartETag(ele.getPartNumber(), ele.getETag());
                })
                .collect(Collectors.toList());

        CompleteMultipartUploadRequest completeMultipartUploadRequest =
             new CompleteMultipartUploadRequest(bucketName, mergeChipFileDto.getResourceId(), uploadId,etagList);
        ossClient.completeMultipartUpload(completeMultipartUploadRequest);
        return new SeedUploadFileVo(mergeChipFileDto.getResourceId(),getFileUrl(bucketName,mergeChipFileDto.getResourceId()));
    }

    @Override
    public CancleChipFileVo cancleChipFile(CancleChipFileDto cancleChipFileDto, SeedUploadChipFileExtDto seedUploadChipFileExtDto) {
        String bucketName = (String)((Map<String, Object>) (seedUploadChipFileExtDto.getAddExtInfo().getOrDefault("extInfo", Collections.EMPTY_MAP))).get("bucketName");
        AbortMultipartUploadRequest param = new AbortMultipartUploadRequest(bucketName,cancleChipFileDto.getResourceId(),String.valueOf(((Map<String,Object>)(seedUploadChipFileExtDto.getAddExtInfo().getOrDefault("extInfo",Collections.EMPTY_MAP))).get("uploadId")));
        ossClient.abortMultipartUpload(param);
        return new CancleChipFileVo(cancleChipFileDto.getResourceId());
    }

    @Override
    public String getContractType() {
        return "ALI";
    }

    @Override
    public Boolean jurgeInstance(String requestUrl) {
        FileConfigAliProp fileConfigProp = ((FileConfigAliProp) (getFileConfigProp()));
        UrlBuilder urlBuilder = UrlBuilder.of(requestUrl);
        int mode = 0;
        if (0 == requestUrl.indexOf("http://")){
            mode = 1;
        } else if (0 == requestUrl.indexOf("https://")){
            mode = 2;
        }
        String substring = null;
        if (mode == 1){
            substring = requestUrl.substring("http://".length());
        }else if (mode == 2){
            substring = requestUrl.substring("https://".length());
        }

        if (0 == substring.indexOf(getFileConfigAliProp().getBucketName()+"."+getFileConfigAliProp().getEndpoint()))
            return true;
        return false;
    }

    @Override
    public ResourceUrlDividVo sliceRequestUrl(String requestUrl) {
        FileConfigAliProp fileConfigProp = ((FileConfigAliProp) (getFileConfigProp()));
        UrlBuilder urlBuilder = UrlBuilder.of(requestUrl);
        int mode = 0;
        if (0 == requestUrl.indexOf("http://")){
            mode = 1;
        } else if (0 == requestUrl.indexOf("https://")){
            mode = 2;
        }
        String substring = null;
        if (mode == 1){
            substring = requestUrl.substring("http://".length());
        }else if (mode == 2){
            substring = requestUrl.substring("https://".length());
        }

        String allPrefix = getFileConfigAliProp().getBucketName() + "." + getFileConfigAliProp().getEndpoint();
        if (0 == substring.indexOf(allPrefix)){
            return new ResourceUrlDividVo(this,substring.substring(allPrefix.length() +1));
        }
        return new ResourceUrlDividVo(this,null);
    }

    @Override
    public void destroy() {
        try {
            ossClient.shutdown();
        } catch (Exception e) {
            log.error("ali oss关闭失败{}",e);
        }
    }



    public FileConfigAliProp getFileConfigAliProp(){
        return (FileConfigAliProp) (this.getFileConfigProp());
    }

    public String getFileUrl(String bucketName,String resourceId){
//        https://{Bucket名称}.{Endpoint}/{ObjectKey}
        return "https://" + SeedStringUtil.concatStr(Arrays.asList(bucketName+"."+getFileConfigAliProp().getEndpoint(),resourceId),"/");
    }
}
